package com.wuliu.system.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wuliu.common.constant.CacheConstants;
import com.wuliu.common.core.redis.RedisCache;
import com.wuliu.common.exception.ServiceException;
import com.wuliu.common.utils.DateUtils;
import com.wuliu.common.utils.DictUtils;
import com.wuliu.common.utils.StringUtils;
import com.wuliu.common.core.domain.entity.BasCust;
import com.wuliu.system.domain.TolPush;
import com.wuliu.system.domain.dto.CustInfoDto;
import com.wuliu.system.domain.vo.BasCustVo;
import com.wuliu.system.mapper.BasCustMapper;
import com.wuliu.system.mapper.TolPushMapper;
import com.wuliu.system.service.FileUploadService;
import com.wuliu.system.service.WeixinService;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author realue
 * @version 1.0
 * @date 2023-10-05 16:33
 */
@Service
public class WeixinServiceImpl implements WeixinService {
    private static final Logger logger = LoggerFactory.getLogger(WeixinServiceImpl.class);
    @Resource
    private RedisCache redisCache;
    @Resource
    private BasCustMapper basCustMapper;
    @Resource
    private TolPushMapper tolPushMapper;
    @Resource
    private FileUploadService fileUploadService;

    /**
     * 获取小程序实例
     *
     * @param sysAccount
     * @return
     */
    private WxMaService getWxMaService(String sysAccount) {
        String appId = DictUtils.getSysConfigValue(sysAccount, "weixin_applet_app_id");
        String secret = DictUtils.getSysConfigValue(sysAccount, "weixin_applet_secret");
        String apiKey = DictUtils.getSysConfigValue(sysAccount, "weixin_applet_api_key");
        if (StringUtils.isAnyBlank(appId, apiKey, secret)) {
            throw new ServiceException("小程序参数配置不正确");
        }
        WxMaService maService = new WxMaServiceImpl();
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
        config.setAppid(appId);
        config.setSecret(secret);
        config.setToken(apiKey);
        config.setMsgDataFormat("JSON");
        Map<String, WxMaConfig> map = new HashMap<>();
        map.put(config.getAppid(), config);
        maService.setMultiConfigs(map);
        return maService;
    }

    /**
     * 获取公众号实例
     *
     * @param sysAccount
     * @return
     */
    private WxMpService getWxMpService(String sysAccount) {
        String appId = DictUtils.getSysConfigValue(sysAccount, "weixin_public_app_id");
        String secret = DictUtils.getSysConfigValue(sysAccount, "weixin_public_secret");
        String apiKey = DictUtils.getSysConfigValue(sysAccount, "weixin_public_api_key");
        if (StringUtils.isAnyBlank(appId, apiKey, secret)) {
            throw new ServiceException("公众号参数配置不正确");
        }
        WxMpService service = new WxMpServiceImpl();
        WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
        config.setAppId(appId);
        config.setSecret(secret);
        config.setToken(apiKey);
        config.setAesKey("");
        Map<String, WxMpConfigStorage> map = new HashMap<>();
        map.put(config.getAppId(), config);
        service.setMultiConfigStorages(map);
        return service;
    }

    /**
     * 获取小程序用户openId
     *
     * @param code
     * @param sysAccount
     * @return
     * @throws WxErrorException
     */
    @Override
    public WxMaJscode2SessionResult getJsCode(String code, String sysAccount) throws WxErrorException {
        WxMaService wxMaService = getWxMaService(sysAccount);
        return wxMaService.getUserService().getSessionInfo(code);
    }

    /**
     * 获取用户信息
     *
     * @param custInfoDto
     * @return
     */
    private WxMaUserInfo getUserInfo(CustInfoDto custInfoDto) {
        WxMaService wxMaService = getWxMaService(custInfoDto.getSysAccount());
        if (!wxMaService.getUserService().checkUserInfo(custInfoDto.getSessionKey(), custInfoDto.getRawData(), custInfoDto.getSignature())) {
            WxMaConfigHolder.remove();
            throw new ServiceException("用户信息校验失败");
        }
        WxMaUserInfo wxMaUserInfo = wxMaService.getUserService().getUserInfo(custInfoDto.getSessionKey(), custInfoDto.getEncryptedData(), custInfoDto.getIv());
        WxMaConfigHolder.remove();
        return wxMaUserInfo;
    }

    /**
     * 小程序登录
     *
     * @param custInfoDto
     * @return
     */
    @Override
    public BasCustVo login(CustInfoDto custInfoDto) {
        BasCustVo retCust = new BasCustVo();
        WxMaUserInfo userInfo = getUserInfo(custInfoDto);
        BasCust basCust = basCustMapper.selectOne(new LambdaQueryWrapper<BasCust>()
                .eq(BasCust::getSysAccount, custInfoDto.getSysAccount())
                .eq(BasCust::getAppletOpenId, custInfoDto.getOpenId()));
        if (null == basCust) {
            basCust = new BasCust();
            basCust.setId(IdUtil.randomUUID());
            basCust.setCustName(userInfo.getNickName());
            basCust.setNickName(userInfo.getNickName());
            basCust.setProvince(userInfo.getProvince());
            basCust.setCity(userInfo.getCity());
            basCust.setDistrict(userInfo.getCountry());
            basCust.setAvatar(userInfo.getAvatarUrl());
            basCust.setAppletOpenId(custInfoDto.getOpenId());
            basCust.setUnionId(userInfo.getUnionId());
            basCust.setCreateTime(DateUtils.getNowDate());
            basCust.setCreateBy("system");
            basCust.setSex(Integer.parseInt(userInfo.getGender()));
            basCustMapper.insertBasCust(basCust);
        }
        BeanUtil.copyProperties(basCust, retCust);
        String token = IdUtil.randomUUID();
        redisCache.setCacheObject(CacheConstants.TOKEN_WEIXIN_LOGIN_INFO_KEY + token, JSONUtil.toJsonStr(basCust), 1, TimeUnit.DAYS);
        retCust.setToken(token);
        return retCust;
    }

    /**
     * 获取微信绑定的手机号
     *
     * @param code
     * @param sysAccount
     * @return
     */
    @Override
    public String getPhone(String code, String sysAccount) throws WxErrorException {
        WxMaService wxMaService = getWxMaService(sysAccount);
        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(code);
        WxMaConfigHolder.remove();
        return phoneNoInfo.getPhoneNumber();
    }

    /**
     * 推送公众号/小程序消息
     *
     * @param tolPush
     */
    @Override
    public void pushWxMessage(TolPush tolPush) {
        if (StringUtils.isNotEmpty(tolPush.getPushContent())) {
            String message = "";
            try {
                Map<String, Object> map = JSONObject.parseObject(tolPush.getPushContent(), Map.class);
                //公众号
                if (tolPush.getPushType() == 2) {
                    WxMpService wxMpService = getWxMpService(tolPush.getSysAccount());
                    List<WxMpTemplateData> templateDataList = new ArrayList<>();
                    for (Map.Entry<String, Object> item : map.entrySet()) {
                        WxMpTemplateData wxMpTemplateData = new WxMpTemplateData();
                        wxMpTemplateData.setName(item.getKey());
                        JSONObject obj = JSONObject.parseObject(item.getValue().toString());
                        wxMpTemplateData.setValue(obj.getString("value"));
                        if (obj.containsKey("color")) {
                            wxMpTemplateData.setColor(obj.getString("color"));
                        }
                        templateDataList.add(wxMpTemplateData);
                    }
                    WxMpTemplateMessage wxMpTemplateMessage = WxMpTemplateMessage.builder()
                            .templateId(tolPush.getTmpId()).toUser(tolPush.getOpenId())
                            .build();
                    if (StringUtils.isNotEmpty(tolPush.getJumpUrl())) {
                        wxMpTemplateMessage.setUrl(tolPush.getJumpUrl());
                    }
                    wxMpTemplateMessage.setData(templateDataList);
                    try {
                        wxMpService.getTemplateMsgService().sendTemplateMsg(wxMpTemplateMessage);
                        message = "ok";
                    } catch (Exception e) {
                        message = e.getMessage();
                    }
                    //小程序
                } else if (tolPush.getPushType() == 1) {
                    WxMaService wxMaService = getWxMaService(tolPush.getSysAccount());
                    List<WxMaSubscribeMessage.MsgData> msgData = new ArrayList<>();
                    for (Map.Entry<String, Object> item : map.entrySet()) {
                        WxMaSubscribeMessage.MsgData data = new WxMaSubscribeMessage.MsgData();
                        data.setName(item.getKey());
                        JSONObject obj = JSONObject.parseObject(item.getValue().toString());
                        data.setValue(obj.getString("value"));
                        msgData.add(data);
                    }
                    WxMaSubscribeMessage wxMaSubscribeMessage = WxMaSubscribeMessage.builder()
                            .toUser(tolPush.getOpenId()).templateId(tolPush.getTmpId()).data(msgData)
                            .build();
                    if (StringUtils.isNotEmpty(tolPush.getJumpUrl())) {
                        wxMaSubscribeMessage.setPage(tolPush.getJumpUrl());
                    }
                    try {
                        wxMaService.getMsgService().sendSubscribeMsg(wxMaSubscribeMessage);
                        message = "ok";
                    } catch (Exception e) {
                        message = e.getMessage();
                    }
                }
            } finally {
                TolPush upPush = new TolPush();
                upPush.setId(tolPush.getId());
                upPush.setPushTime(DateUtils.getTime());
                upPush.setPushResult(message);
                if ("ok".equals(message)) {
                    upPush.setStatus(1);
                } else {
                    upPush.setStatus(2);
                }
                tolPushMapper.updateTolPush(upPush);
            }
        }
    }

    /**
     * 生成带参数的小程序二维码
     *
     * @param page  跳转页面
     * @param scene 参数
     * @return 返回图片地址
     */
    @Override
    public String getUnlimited(String page, String scene, String sysAccount) throws WxErrorException {
        WxMaService wxMaService = getWxMaService(sysAccount);
        File file = wxMaService.getQrcodeService().createWxaCodeUnlimit(scene, page);
        return fileUploadService.uploadFile(file, "");
    }
}
